﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Runtime.Remoting;
using System.Reflection;
using System.Xml;
using System.IO;

namespace TFDP.Common.Configuration
{
    public class SettingsCollection : List<ISetting>, IXmlSerializable
    {
        [XmlIgnore]
        public const string Namespace = "http://tfdp.com/2010/Settings";

        // Indexer
        public ISetting this[string key]
        {
            get
            {
                var match = from setting in this
                            where setting.Key == key
                            select setting;

                if (match.Count() != 1)
                    return null;
                else
                    return match.First();
            }
        }

        public bool ContainsKey(string key)
        {
            return this.Any(setting => setting.Key == key);
        }

        public T Get<T>(string key)
        {
            var match = from setting in this
                        where setting.Key == key
                        select setting.Value;

            if (match.Count() != 1)
                return default(T);
            else
                return (T)match.First();
        }

        private ISetting GetGenericSetting(ISetting setting)
        {
            Type constructedGeneric = typeof(Setting<>).MakeGenericType(new Type[] { setting.ValueType });
            ISetting retval = (ISetting)Activator.CreateInstance(constructedGeneric);

            return retval;
        }

        #region IXmlSerializable Members

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            if (reader.NodeType == XmlNodeType.None)
                if (!reader.Read())
                    return;

            while (reader.ReadToFollowing("Setting"))
            {
                if (reader.HasAttributes)
                {
                    string key = reader.GetAttribute("Key");
                    string valueTypeName = reader.GetAttribute("ValueType");

                    Type valueType = Type.GetType(valueTypeName);
                    Type interfaceType = valueType.GetInterface(typeof(IXmlSerializable).FullName);
                    
                    Type constructedGeneric = typeof(Setting<>).MakeGenericType(new Type[] { valueType });
                    ISetting setting = (ISetting)Activator.CreateInstance(constructedGeneric);
                    setting.Key = key;

                    ObjectHandle handle = null;

                    if(interfaceType == null)
                    {
                        object val = reader.ReadContentAs(valueType, null);
                        handle = new ObjectHandle(val);
                        setting.Value = handle.Unwrap();
                    }
                    else
                    {
                        XmlReader subReader = XmlReader.Create(reader, new XmlReaderSettings());
                        ((IXmlSerializable)setting.Value).ReadXml(subReader);
                    }

                    this.Add(setting);
                }
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            foreach (var setting in this)
            {
                writer.WriteStartElement("Setting", Namespace);
                writer.WriteAttributeString("Key", setting.Key);
                writer.WriteAttributeString("ValueType", setting.ValueType.AssemblyQualifiedName);

                if (setting.Value != null)
                {
                    if (!(setting.Value is IXmlSerializable))
                    {
                        writer.WriteStartAttribute("Value");
                        writer.WriteValue(setting.Value.ToString());
                        writer.WriteEndAttribute();
                    }
                    else
                    {
                        writer.WriteStartElement("Value", Namespace);

                        ((IXmlSerializable)setting.Value).WriteXml(writer);

                        writer.WriteEndElement();
                    }
                }

                writer.WriteEndElement();
            }
        }

        #endregion

        #region Statics
        private const string xmlHeader = "<?xml version=\"1.0\"?>\r\n";

        public static string SerializeSettings(SettingsCollection settings)
        {
            try
            {
                XmlSerializer serializer = new XmlSerializer(settings.GetType(), null, null, null, "http://tfdp.com/2010/Settings");
                MemoryStream stream = new MemoryStream();

                serializer.Serialize(stream, settings);

                byte[] buff = stream.GetBuffer();

                string retval = System.Text.ASCIIEncoding.ASCII.GetString(buff);
                return retval.Replace(xmlHeader, String.Empty);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public static SettingsCollection DeserializeSettings(string value)
        {
            string toDeserialize = xmlHeader + value;
            XmlSerializer serializer = new XmlSerializer(typeof(SettingsCollection), "http://tfdp.com/2010/Settings");

            MemoryStream stream = new MemoryStream();
            byte[] buff = System.Text.ASCIIEncoding.ASCII.GetBytes(toDeserialize);
            stream.Write(buff, 0, buff.Length);
            stream.Seek(0, SeekOrigin.Begin);

            try
            {
                SettingsCollection settings = (SettingsCollection)serializer.Deserialize(stream);

                return settings;
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion
    }
}
